package org.scau.studentevaluation.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.extern.slf4j.Slf4j;
import org.scau.studentevaluation.client.ActClient;
import org.scau.studentevaluation.client.AttendedStudentListClient;
import org.scau.studentevaluation.client.ExamClient;
import org.scau.studentevaluation.client.OtherActClient;
import org.scau.studentevaluation.consts.StudentConst;
import org.scau.studentevaluation.entity.bo.CheckTableItem;
import org.scau.studentevaluation.entity.dos.ActDO;
import org.scau.studentevaluation.entity.dos.StudentDO;
import org.scau.studentevaluation.entity.enums.IsExistEnum;
import org.scau.studentevaluation.entity.enums.IsPassedEnum;
import org.scau.studentevaluation.entity.query.ActExistQuery;
import org.scau.studentevaluation.entity.query.ExamQuery;
import org.scau.studentevaluation.entity.query.GetOtherActQuery;
import org.scau.studentevaluation.entity.query.StudentQuery;
import org.scau.studentevaluation.entity.vo.CheckTableVO;
import org.scau.studentevaluation.entity.vo.OtherActVO;
import org.scau.studentevaluation.entity.vo.StudentVO;
import org.scau.studentevaluation.mapper.StudentMapper;
import org.scau.studentevaluation.service.StudentService;
import org.scau.studentevaluation.util.BeanConvertUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.validation.Valid;
import javax.validation.constraints.Min;
import java.time.Year;
import java.time.YearMonth;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * @ClassName: StudentServiceImpl
 * @Description: TODO
 * @Author: sunslikes
 * @Date: 2020/8/27 18:29
 * @Version: 1.0
 */
@Slf4j
@Service
public class StudentServiceImpl implements StudentService {

    private StudentMapper studentMapper;
    private ExamClient examClient;
    private ActClient actClient;
    private OtherActClient otherActClient;
    private AttendedStudentListClient attendedStudentListClient;

    @Autowired
    public StudentServiceImpl(StudentMapper studentMapper, ExamClient examClient, ActClient actClient, OtherActClient otherActClient, AttendedStudentListClient attendedStudentListClient) {
        this.studentMapper = studentMapper;
        this.examClient = examClient;
        this.actClient = actClient;
        this.otherActClient = otherActClient;
        this.attendedStudentListClient = attendedStudentListClient;
    }

    @Override
    public List<StudentVO> listStudentsByCollege(String college) {
        LambdaQueryWrapper<StudentDO> wrapper =
                Wrappers.lambdaQuery(StudentDO.class)
                    .eq(StudentDO::getCollege, college);
        List<StudentDO> studentDOS = studentMapper.selectList(wrapper);
        List<StudentVO> studentVOS = BeanConvertUtils.BOs2VOs(studentDOS, StudentVO.class);
        return studentVOS;
    }

    @Override
    public List<Integer> listStudentYears(String studentId) {
        // 获取学生入学时间
        final int startYear = Integer.valueOf(studentId.substring(0, 4));
        // 算一下当前综测年份
        final int endYear = YearMonth.now().getMonthValue() > StudentConst.CUT_MONTH?
                Year.now().getValue(): Year.now().getValue() - 1;
        // 我还以为这么写性能不佳，结果发现stream牛啊
        return IntStream.rangeClosed(startYear, endYear).boxed().limit(StudentConst.MAX_STUDENT_IN_SCHOOL_TIME).collect(Collectors.toList());
        // 本来以为这样循环最多五次性能提升，结果看来stream的原理和循环不太一样，应该是stream的boxed中间操作惰性化和limit短路操作让它尽早结束吧
//        return IntStream.rangeClosed(0, endYear - startYear + 1 < StudentConst.MAX_STUDENT_IN_SCHOOL_TIME? endYear - startYear: StudentConst.MAX_STUDENT_IN_SCHOOL_TIME - 1)
//                .boxed()
//                .map(integer -> startYear + integer)
//                .collect(Collectors.toList());
    }

    /**
     * 14. 获取综测评分表(原StudentController中的getRewardTable方法)
     * 	入参为StudentQuery(原Student类)和int activityYear
     *
     * 	通过studentId、other_activity_year、is_passed从otheract中获取信息（通过feign调用other_act模块中获取其他活动2的方法）
     *
     * 	通过other_act_id从exam中获取信息（通过feign调用exam模块中查询exam信息的方法）
     *
     * 	通过college、ruleYear、passed从exam中获取actId（通过feign调用exam模块中查询actId的方法）
     *
     * 	通过actId、studentId从attendedstudentlist中获取actId（通过feign调用attendedstudentlist模块中判断指特定学生和活动的数据是否存在的方法）
     *
     * 	通过actId从act中获取act列表（通过feign调用act模块中获取Act的方法）
     *
     * 	通过actId从exam中获取exam列表（通过feign调用exam模块中获取Act的方法）
     * @param studentQuery 学生
     * @param activityYear 学年
     * @return
     */
    @Override
    public CheckTableVO getRewardTable(StudentQuery studentQuery, int activityYear) {
        CheckTableVO checkTableVO = new CheckTableVO();
        checkTableVO.setFirstList(new ArrayList<>());
        checkTableVO.setSecondList(new ArrayList<>());
        checkTableVO.setThirdList(new ArrayList<>());
        List<OtherActVO> otherActVOList =
                Optional.ofNullable(otherActClient.getOtherActs(Long.valueOf(studentQuery.getStudentId()), String.valueOf(activityYear), IsPassedEnum.IS_PASSED_TRUE.getType()))
                        .orElse(new ArrayList<>());
        log.info(otherActVOList.stream().map(otherActVO -> otherActVO.toString()).collect(Collectors.joining(",", "List[", "]")));
        for (OtherActVO otherActVO: otherActVOList) {
            // 这样取到空数组的首个元素也不会报错
            Optional<CheckTableItem> checkTableItemOptional = examClient.listExamByOtherActId(otherActVO.getId()).stream().findFirst();
            if (!checkTableItemOptional.isPresent()) {
                continue;
            }
            CheckTableItem checkTableItem = checkTableItemOptional.get();
            checkTableItem.setTitle(otherActVO.getOtherActName());
            switch (checkTableItem.getSubject()) {
                case 0:
                    checkTableVO.getFirstList().add(checkTableItem);
                    break;
                case 1:
                    checkTableVO.getSecondList().add(checkTableItem);
                    break;
                case 2:
                    checkTableVO.getThirdList().add(checkTableItem);
            }
        }

        // 社团列表部分
        List<ActDO> actList = getPassedActivities(studentQuery, activityYear);
        for (ActDO act : actList) {
            Optional<CheckTableItem> checkTableItemOptional = examClient.listExamByActId(act.getId()).stream().findFirst();
            if (checkTableItemOptional.isPresent()) {
                continue;
            }
            CheckTableItem checkTableItem = checkTableItemOptional.get();
            checkTableItem.setTitle(act.getActName());
            switch (checkTableItem.getSubject()) {
                case 0:
                    checkTableVO.getFirstList().add(checkTableItem);
                    break;
                case 1:
                    checkTableVO.getSecondList().add(checkTableItem);
                    break;
                case 2:
                    checkTableVO.getThirdList().add(checkTableItem);
            }
        }
        return checkTableVO;
    }

    private List<ActDO> getPassedActivities(StudentQuery student, int activityYear) {
        List<Long> actIds = Optional.ofNullable(examClient.listActId(student.getCollege(), activityYear, IsPassedEnum.IS_PASSED_TRUE.getType()))
                .orElse(new ArrayList<>());

        if (actIds.isEmpty()) {
            return new ArrayList<>();
        }
        List<Long> realactIds = new ArrayList<>();
        for (long actId : actIds) {
            if (attendedStudentListClient.ActExist(actId, Long.valueOf(student.getStudentId())) > IsExistEnum.IS_EXIST_ENUM.getType()) {
                realactIds.add(actId);
            }
        }
        List<ActDO> actList = new ArrayList<>();
        for (long i : realactIds) {
            ActDO act = actClient.findAct(i);
            if(act != null) {
                actList.add(act);
            }
        }
        log.info(actList.stream().map(actDO -> actDO.toString()).collect(Collectors.joining(",", "ActList[", "]")));
        return actList;
    }


}
